#!/usr/bin/python2.4
#
# Copyright 2009-2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================================================================


import omaha_version_utils
import os

Import('env')

def BuildCOMForwarder(cmd_line_switch,
                      signed_exe_name,
                      should_append_cmd_line):
  com_forwarder_env = env.Clone()

  com_forwarder_env.FilterOut(CCFLAGS=['/GL', '/RTC1',])
  com_forwarder_env.FilterOut(LINKFLAGS=['/LTCG'])

  should_append_string = 'FALSE'
  if should_append_cmd_line:
    should_append_string = 'TRUE'

  com_forwarder_env.Append(
      CCFLAGS=[
          '/Zl',
      ],
      CPPDEFINES = [
          'CMD_LINE_SWITCH=_T(\\"%s\\")' % cmd_line_switch,
          'SHOULD_APPEND_CMD_LINE=%s' % should_append_string,
      ],
      LINKFLAGS=[
          '/ENTRY:wWinMainCRTStartup',
      ],
      LIBS=[
          com_forwarder_env['crt_libs'][com_forwarder_env.Bit('debug')],
          'shlwapi.lib',
      ],
  )
  # The resource file used is the same as the one for GoogleUpdate.exe.
  # The version resource used is the same as the one for goopdate.dll.
  com_forwarder_res = com_forwarder_env.RES('%s.res' % signed_exe_name,
                                            '../google_update/resource.rc')
  com_forwarder_env.Depends(com_forwarder_res,
                            '../google_update/GoogleUpdate.manifest')
  com_forwarder_inputs = [
       com_forwarder_env.ComponentObject('%s.obj' % signed_exe_name,
                                         'com_forwarder.cc'),
       com_forwarder_res,
       '$OBJ_ROOT/goopdate/goopdate_version.res',
  ]

  unsigned_broker = com_forwarder_env.ComponentProgram(
      prog_name='%s_unsigned' % signed_exe_name,
      source=com_forwarder_inputs,
  )
  signed_broker = com_forwarder_env.SignedBinary(
      target='%s.exe' % signed_exe_name,
      source=unsigned_broker,
  )
  env.Replicate('$STAGING_DIR', signed_broker)


# Build the broker/legacy on-demand/WebPlugin forwarders.
BuildCOMForwarder('/broker', 'GoogleUpdateBroker', False)
BuildCOMForwarder('/ondemand', 'GoogleUpdateOnDemand', False)

def BuildCOMRegisterShell64():
  com_register_shell_env = env.CloneAndMake64Bit()
  com_register_shell_env.FilterOut(CCFLAGS=['/GL', '/RTC1',])
  com_register_shell_env.FilterOut(LINKFLAGS=['/LTCG'])

  com_register_shell_env.Append(
      CCFLAGS=[
          '/Zl',
      ],
      LINKFLAGS=[
          '/ENTRY:WinMainCRTStartup',
      ],
      LIBS=[
          '$LIB_DIR/base_64.lib',
          '$LIB_DIR/common_64.lib',
          '$LIB_DIR/logging_64.lib',
          '$LIB_DIR/goopdate_lib_64.lib',
          com_register_shell_env['atls_libs'][
              com_register_shell_env.Bit('debug')
          ],
          com_register_shell_env['crt_libs'][
              com_register_shell_env.Bit('debug')],
          'delayimp.lib',
          'netapi32.lib',
          'psapi.lib',
          'shlwapi.lib',
          'version.lib',
          'userenv.lib',
          'wtsapi32.lib',
      ],
  )
  if env.Bit('has_device_management'):
    com_register_shell_env.Append(
        LIBS=[
            '$LIB_DIR/dm_proto_64.lib',
            '$LIB_DIR/libprotobuf_64.lib',
        ],
    )

  # Add an icon, a manifest, and a version to the exe.
  # The resource file used is the same as the one for GoogleUpdate.exe.
  # The version resource used is the same as the one for goopdate.dll.
  signed_exe_name = 'GoogleUpdateComRegisterShell64'
  com_register_shell_res = com_register_shell_env.RES(
                               '%s.res' % signed_exe_name,
                               '../google_update/resource.rc')
  com_register_shell_env.Depends(com_register_shell_res,
                                 '../google_update/GoogleUpdate.manifest')
  com_register_shell_inputs = [
       com_register_shell_env.ComponentObject('%s.obj' % signed_exe_name,
                                              'com_register_shell.cc'),
       com_register_shell_res,
       '$OBJ_ROOT/goopdate/goopdate_version.res',
  ]

  unsigned_exe = com_register_shell_env.ComponentProgram(
      prog_name='%s_unsigned' % signed_exe_name,
      source=com_register_shell_inputs
  )
  signed_exe = com_register_shell_env.SignedBinary(
      target='%s.exe' % signed_exe_name,
      source=unsigned_exe,
  )
  env.Replicate('$STAGING_DIR', signed_exe)

# Build the COM register shell.
BuildCOMRegisterShell64()

#
# Generate omaha3_idl.idl. The output is an IDL file with a variant CLSID
# for coclass GoogleComProxyMachineClass and GoogleComProxyUserClass.
#
def GenerateOmaha3IDLFile():
  return env.Command(
      target='omaha3_idl.idl',
      source='$MAIN_DIR/goopdate/omaha3_idl.idl',
      action=('python "%s/tools/generate_omaha3_idl.py" --idl_template_file '
          '$SOURCE --idl_output_file $TARGET' % env['MAIN_DIR'])
  )

#
# Build omaha3_idl COM proxy.
#
def BuildGoogleUpdateCOMProxy(midl_env, generated_idl):
  midl_env.Tool('midl')
  midl_env['MIDLFLAGS'] += [
      '/Oicf',  # generate optimized stubless proxy/stub code
      ]

  suffix = ('', '_64')[midl_env.Bit('x64')]
  if midl_env.Bit('x64'):
    midl_env['MIDLFLAGS'] += [ '/amd64' ]

    # Make a copy of the IDL file with different name so the output won't
    # collide with the output from 32-bit version IDL.
    target_idl = 'omaha3_idl_64.idl',
    midl_env.Command(target_idl, 'omaha3_idl.idl', Copy('$TARGET', '$SOURCE'))
    midl_env.TypeLibrary(target_idl)
  else:
    midl_env['MIDLFLAGS'] += [ '/win32' ]
    midl_outputs = midl_env.TypeLibrary('omaha3_idl.idl')

    # Save the .idl and the produced .tlb and .h files so we can provide
    # them to clients. This process is needed only for 32-bit since Omaha is
    # 32-bit only.
    midl_env.Replicate('$STAGING_DIR/idls', 'omaha3_idl.idl')
    for node in midl_outputs:
      if not str(node).endswith('.c'):
         midl_env.Replicate('$STAGING_DIR/idls', node)

  # Generate the interface library as an object file.
  midl_lib_inputs = [
      'omaha3_idl%s_i.c' % suffix,
      ]

  midl_env.ComponentLibrary(
      'omaha3_idl%s' % suffix,
      midl_lib_inputs,
  )

generated_omaha3_idl = GenerateOmaha3IDLFile()
BuildGoogleUpdateCOMProxy(env.Clone(), generated_omaha3_idl)
BuildGoogleUpdateCOMProxy(env.CloneAndMake64Bit(), generated_omaha3_idl)

def BuildGoogleUpdateHandlerDll(handler_env,
                                omaha_version_info,
                                is_machine_handler,
                                psname):
  handler_env.Append(
      CPPDEFINES = [
          '_ATL_FREE_THREADED',
          '_USRDLL',
          '_WINDLL',
      ],
      CCFLAGS = [
        '/wd4263',
        '/wd4264',
        '/wd4255',
        '/wd4426',
        '/wd5038',
      ],
      LIBS = [
          handler_env.GetMultiarchLibName('base'),
          handler_env.GetMultiarchLibName('goopdate_lib'),
          handler_env.GetMultiarchLibName('security'),
          handler_env['atls_libs'][env.Bit('debug')],
          handler_env['crt_libs'][handler_env.Bit('debug')],
          'psapi.lib',
          'netapi32.lib',
          'shlwapi.lib',
          'userenv.lib',
          'version.lib',
          'wtsapi32.lib',
      ],
  )
  if handler_env.Bit('has_device_management'):
    handler_env.Append(
        LIBS = [
            handler_env.GetMultiarchLibName('dm_proto'),
            handler_env.GetMultiarchLibName('libprotobuf'),
        ],
    )

  version_string = omaha_version_info.GetVersionString()
  prefix = omaha_version_info.filename_prefix

  if prefix:
    handler_env['OBJPREFIX'] = handler_env.subst(prefix + 'obj/$OBJPREFIX')

  handler_env.Append(
      CPPDEFINES = [
          'IS_MACHINE_HANDLER=%d' % is_machine_handler,
      ],
      LIBS = [
          handler_env.GetMultiarchLibName('common'),
          'crypt32.lib',
          'wininet.lib',
          'rpcrt4.lib',
      ],
      RCFLAGS = [
          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
          '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
      ],
  )

  suffix = ('', '_64')[handler_env.Bit('x64')]
  resource = handler_env.RES(
      target='%s%s_resource%s.res' % (prefix, psname, suffix),
      source='google_update_ps_resource.rc')

  handler_env.Depends(
      resource,
      ['$MAIN_DIR/VERSION',
       '$MAIN_DIR/base/generic_reg_file_dll_handler.rgs'])

  target_name = '%s%s_unsigned%s' % (prefix, psname, suffix)

  inputs = [
      'google_update_ps.def',
      resource,
      prefix + 'goopdate_version.res',
      ]
  inputs += handler_env.Object('google_update_ps_%s%s.obj' % (psname, suffix),
                               '$OBJ_ROOT/goopdate/google_update_ps.cc')
  inputs += handler_env.Object('omaha3_idl_datax_%s%s.obj' % (psname, suffix),
                               '$OBJ_ROOT/goopdate/omaha3_idl_datax.c')
  unsigned_dll = handler_env.ComponentLibrary(
      lib_name=target_name,
      source=inputs,
  )

  signed_dll = handler_env.SignedBinary(
      target='%s%s%s.dll' % (prefix, psname, suffix),
      source=unsigned_dll,
  )

  env.Replicate('$STAGING_DIR', signed_dll)
  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])


handler_env = env.Clone(COMPONENT_STATIC = False)
for omaha_version_info in env['omaha_versions_info']:
  BuildGoogleUpdateHandlerDll(handler_env.Clone(),
                              omaha_version_info, 1, 'psmachine')
  BuildGoogleUpdateHandlerDll(handler_env.CloneAndMake64Bit(),
                              omaha_version_info, 1, 'psmachine')
  BuildGoogleUpdateHandlerDll(handler_env.Clone(),
                              omaha_version_info, 0, 'psuser')
  BuildGoogleUpdateHandlerDll(handler_env.CloneAndMake64Bit(),
                              omaha_version_info, 0, 'psuser')

if env.Bit('has_device_management'):
  # Compile the device management server protobufs.
  proto_env = env.Clone()
  proto_env.Append(PROTO_PATH = '$GOOGLE3')
  proto_env.Append(CPP_OUT = '$TARGET_ROOT/proto_files')
  proto_sources = [
      '$GOOGLE3/devtools/staticanalysis/pipeline/analyzers/' +
          'proto_best_practices/proto/optouts.proto',
      '$GOOGLE3/logs/proto/logs_annotations/logs_annotations.proto',
      '$GOOGLE3/net/proto2/bridge/proto/message_set.proto',
      '$GOOGLE3/net/proto2/proto/descriptor.proto',
      '$GOOGLE3/privacy/private_membership/proto/private_membership.proto',
      '$GOOGLE3/privacy/private_membership/rlwe/proto/private_membership_rlwe.proto',
      '$GOOGLE3/storage/datapol/annotations/proto/semantic_annotations.proto',
      '$GOOGLE3/storage/datapol/annotations/proto/retention_annotations.proto',
      '$GOOGLE3/storage/datapol/annotations/proto/datapol_classification.proto',
      '$GOOGLE3/third_party/rlwe/serialization.proto',
      '$GOOGLE3/wireless/android/enterprise/devicemanagement/proto/' +
          'dm_api.proto',
      '$GOOGLE3/wireless/android/enterprise/devicemanagement/proto/' +
          'omaha_settings.proto',
      ]
  proto_outputs = proto_env.CompileProtoBuf(proto_sources)

  # Build the device management messages and protobuf library.
  protobuf_src_dir = os.getenv('OMAHA_PROTOBUF_SRC_DIR',
                               '$GOOGLE3/third_party/protobuf/src')
  proto_env.Append(
      CCFLAGS=[
          '/wd4065',
          '/wd4100',
          '/wd4125',
          '/wd4127',
          '/wd4146',
          '/wd4242',
          '/wd4244',
          '/wd4267',
          '/wd4309',
          '/wd4548',
          '/wd4577',
          '/wd4647',
          '/wd4800',
          '/wd4946',
          '/wd4995',
          '/wd4996',
          ],
      CPPPATH=[
          protobuf_src_dir,
          '$TARGET_ROOT/proto_files',
      ],
   )
  dm_proto_lib = proto_env.ComponentStaticLibraryMultiarch(
      'dm_proto',
      proto_outputs + ['dm_messages.cc'],
  )

gd_env = env.Clone()

# TODO(omaha3): Is it okay that other libs, such as common, do not define this.
gd_env['CPPDEFINES'] += [
    '_ATL_FREE_THREADED',
    ]

# Need to look in output dir to find .h files generated by midl compiler.
gd_env['CPPPATH'] += [
    '$OBJ_ROOT',  # Needed for generated files.
    ]

target_name = 'goopdate_lib'

gd_inputs = [
    'app.cc',
    'app_bundle.cc',
    'app_bundle_state.cc',
    'app_bundle_state_busy.cc',
    'app_bundle_state_init.cc',
    'app_bundle_state_initialized.cc',
    'app_bundle_state_paused.cc',
    'app_bundle_state_ready.cc',
    'app_bundle_state_stopped.cc',
    'app_command.cc',
    'app_command_completion_observer.cc',
    'app_command_configuration.cc',
    'app_command_formatter.cc',
    'app_command_model.cc',
    'app_command_ping_delegate.cc',
    'app_manager.cc',
    'app_state.cc',
    'app_state_error.cc',
    'app_state_init.cc',
    'app_state_checking_for_update.cc',
    'app_state_download_complete.cc',
    'app_state_downloading.cc',
    'app_state_install_complete.cc',
    'app_state_installing.cc',
    'app_state_no_update.cc',
    'app_state_ready_to_install.cc',
    'app_state_update_available.cc',
    'app_state_waiting_to_check_for_update.cc',
    'app_state_waiting_to_download.cc',
    'app_state_waiting_to_install.cc',
    'app_version.cc',
    'application_usage_data.cc',
    'code_red_check.cc',
    'crash.cc',
    'cocreate_async.cc',
    'cred_dialog.cc',
    'current_state.cc',
    'download_manager.cc',
    'google_app_command_verifier.cc',
    'google_update.cc',
    'goopdate.cc',
    'goopdate_metrics.cc',
    'install_manager.cc',
    'installer_wrapper.cc',
    'job_observer.cc',
    'model.cc',
    'model_object.cc',
    'ondemand.cc',
    'offline_utils.cc',
    'string_formatter.cc',
    'package.cc',
    'package_cache.cc',
    'ping_event_cancel.cc',
    'policy_status.cc',
    'policy_status_value.cc',
    'process_launcher.cc',
    'resource_manager.cc',
    'update3web.cc',
    'update_request_utils.cc',
    'update_response_utils.cc',
    'worker.cc',
    'worker_utils.cc',
    'worker_metrics.cc',
    ]

if gd_env.Bit('has_device_management'):
  gd_inputs += [
      'dm_client.cc',
      'dm_storage.cc',
      ]

# Compile the library.
gd_env.ComponentStaticLibraryMultiarch(target_name, gd_inputs)


#
# Build Goopdate DLL
#
for omaha_version_info in env['omaha_versions_info']:
  temp_env = env.Clone(COMPONENT_STATIC=False)

  prefix = omaha_version_info.filename_prefix
  if prefix:
    temp_env['OBJPREFIX'] = temp_env.subst(prefix + 'obj/$OBJPREFIX')

  # Add languages that have version resources but are not fully supported.
  translated_languages = omaha_version_utils.GetShellLanguagesForVersion(
                             omaha_version_info.GetVersion())

  temp_env.Append(
      CPPPATH = [
          '$OBJ_ROOT',
          ],

      # Do not add static dependencies on system import libraries. Prefer delay
      # loading when possible. Only what is necessary must be loaded in the
      # memory space when long-running.
      LIBS = [
          '$LIB_DIR/base.lib',
          '$LIB_DIR/breakpad.lib',
          '$LIB_DIR/client.lib',
          '$LIB_DIR/common.lib',
          '$LIB_DIR/core.lib',
          '$LIB_DIR/crx_file.lib',
          '$LIB_DIR/google_update_recovery.lib',
          '$LIB_DIR/goopdate_lib.lib',
          '$LIB_DIR/libprotobuf.lib',
          '$LIB_DIR/logging.lib',
          '$LIB_DIR/net.lib',
          '$LIB_DIR/omaha3_idl.lib',
          '$LIB_DIR/security.lib',
          '$LIB_DIR/service.lib',
          '$LIB_DIR/setup.lib',
          '$LIB_DIR/statsreport.lib',
          '$LIB_DIR/ui.lib',
          temp_env['atls_libs'][temp_env.Bit('debug')],
          temp_env['crt_libs'][temp_env.Bit('debug')],
          # TODO(omaha3): This must be linked in because we have UI in the DLL.
          'bits.lib',
          'comctl32.lib',
          'crypt32.lib',
          'delayimp.lib',
          'imagehlp.lib',
          'iphlpapi.lib',
          'msimg32.lib',
          'mstask.lib',
          'netapi32.lib',
          'psapi.lib',
          'rpcns4.lib',
          'rpcrt4.lib',
          'shlwapi.lib',
          'taskschd.lib',
          'version.lib',
          'userenv.lib',
          'uxtheme.lib',
          'wininet.lib',
          'wintrust.lib',
          'ws2_32.lib',
          'wtsapi32.lib',
          ],
      LINKFLAGS = [
          # TODO(Omaha) - Choose a rebase address which does not conflict
          # with other DLLs loaded in our process. For now, we just picked
          # an arbitrary address.
          '/BASE:0x18000000',
          ],
      RCFLAGS = [
          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
          '/DVERSION_NUMBER_STRING=\\"%s\\"' % (
              omaha_version_info.GetVersionString()),

          # goopdate.dll is resource neutral.
          '/DLANGUAGE_STRING=\\"en\\"',
          ],
  )
  if temp_env.Bit('has_device_management'):
    temp_env.Append(
        LIBS = [
          '$LIB_DIR/dm_proto.lib',
        ],
    )

  resource_res = temp_env.RES(
      target=prefix + 'goopdate.res',
      source='goopdate.rc',
  )

  # Force a rebuild when the .tlb changes.
  temp_env.Depends(resource_res, '$OBJ_ROOT/goopdate/omaha3_idl.tlb')

  version_res = temp_env.RES(
      target=prefix + 'goopdate_version.res',
      source='goopdate_version.rc'
  )

  # Force a rebuild when the version changes.
  env.Depends(version_res, '$MAIN_DIR/VERSION')

  target_name = prefix + 'goopdate_unsigned'

  # main.cc is included here because the linker gets confused if we try to
  # create a DLL without an entry point. There's probably a more accurate
  # description of the problem and thus a different solution, but this worked.
  inputs = [
      'goopdate.def',
      'main.cc',
      resource_res,
      version_res,
      ]

  for language in translated_languages:
    lang_base_name = 'goopdate_dll/generated_resources_' + language
    inputs += temp_env.RES(
        target='resources/%s.res' % (prefix + lang_base_name),
        source='resources/%s.rc' % lang_base_name,
    )

  unsigned_dll = temp_env.ComponentLibrary(
      lib_name=target_name,
      source=inputs,
  )

  signed_dll = temp_env.SignedBinary(
      target=prefix + 'goopdate.dll',
      source=unsigned_dll,
  )

  env.Replicate('$STAGING_DIR', signed_dll)
  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])


customization_test_env = env.Clone()

customization_test_env.Append(
    LIBS = [
        '$LIB_DIR/common.lib',
        '$LIB_DIR/security.lib',
        'crypt32.lib',
    ],
)

customization_test_env['CPPPATH'] += [
    '$OBJ_ROOT',  # Needed for generated files.
    ]

# Build all the resource dlls.
env.BuildSConscript('resources')

test_installer_env = env.Clone()

test_installer_env.Append(
  CCFLAGS = [
      '/wd4995',
      ],
  LIBS = [
      'delayimp.lib',
      'advapi32.lib',
      'crypt32.lib',
      'kernel32.lib',
      'netapi32.lib',
      'psapi.lib',
      'shell32.lib',
      'shlwapi.lib',
      'user32.lib',
      'userenv.lib',
      'version.lib',
      'wintrust.lib',
      'wtsapi32.lib',
      test_installer_env['atls_libs'][test_installer_env.Bit('debug')],
      test_installer_env['crt_libs'][test_installer_env.Bit('debug')],
      '$LIB_DIR/base.lib',
      ],
  LINKFLAGS = [
      '/NODEFAULTLIB',
      '/MERGE:.rdata=.text'
      ],
)

test_installer_res = test_installer_env.RES('TestOmahaExeInstaller.res',
                                            '../google_update/resource.rc')
test_installer_env.Depends(test_installer_res,
                           '../google_update/GoogleUpdate.manifest')
test_installer_inputs = [
    test_installer_env.ComponentObject('TestOmahaExeInstaller.obj',
                                       'test_omaha_exe_installer.cc'),
    test_installer_res,
]

unsigned_test_installer = test_installer_env.ComponentProgram(
    prog_name='TestOmahaExeInstaller_unsigned',
    source=test_installer_inputs,
)
signed_test_installer = test_installer_env.SignedBinary(
    target='TestOmahaExeInstaller.exe',
    source=unsigned_test_installer,
)
env.Replicate('$STAGING_DIR', signed_test_installer)

